﻿#region Using Statements
using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.Serialization;
#endregion

namespace DirectZZT.Shared.Core
{
    /// <summary>
    /// Provides one single log entry.
    /// </summary>
    [Serializable()]
    [DebuggerDisplay("Id = {Id}, Source = {Source}, Message = {Message}")]
    public sealed class LogEntry : ISerializable
    {
        #region Properties

        /// <summary>
        /// Gets/sets the Alias of this Entry.
        /// </summary>
        public Nullable<int> Id { get; set; }

        /// <summary>
        /// Gets the <see cref="LoggingMessageType"/>.
        /// </summary>
        public LoggingMessageType MessageType { get; private set; }
        /// <summary>
        /// Gets the source that emitted the log entry.
        /// </summary>
        public string Source { get; private set; }
        /// <summary>
        /// Gets/sets the category of the log entry.
        /// </summary>
        public string Category { get; set; }
        /// <summary>
        /// Gets the message.
        /// </summary>
        public string Message { get; private set; }
        /// <summary>
        /// Gets the time this entry occured.
        /// </summary>
        public DateTime Timestamp { get; private set; }
        /// <summary>
        /// Gets/sets the exception, if this entry is an exception-entry.
        /// </summary>
        public Exception Exception { get; set; }

        #endregion

        #region Constructors

        /// <summary>
        /// Private constructor.
        /// </summary>
        private LogEntry()
        {
            MessageType = LoggingMessageType.None;
            Timestamp = DateTime.Now;
        }

        /// <summary>
        /// Initializes a new instance of the CLogEntry class.
        /// </summary>
        /// <param name="messageType">The message type.</param>
        /// <param name="source">The source that has emitted this log entry.</param>
        /// <param name="message">The message.</param>
        public LogEntry(LoggingMessageType messageType, string source, string message)
            : this(messageType, source, message, DateTime.Now)
        {

        }

        /// <summary>
        /// Initializes a new instance of the CLogEntry class.
        /// </summary>
        /// <param name="messageType">The message type.</param>
        /// <param name="source">The source that has emitted this log entry.</param>
        /// <param name="message">The message.</param>
        /// <param name="timestamp">A custom <see cref="DateTime"/> defining the time stamp of this entry, if different.</param>
        public LogEntry(LoggingMessageType messageType, string source, string message, DateTime timestamp)
            : this()
        {
            MessageType = messageType;
            Source = source;
            Message = message;
            Timestamp = timestamp;
        }

        /// <summary>
        /// Deserialization constructor.
        /// </summary>
        /// <param name="info">The <see cref="System.Runtime.Serialization.SerializationInfo"/> to populate with data.</param>
        /// <param name="context">The destination (see <see cref="System.Runtime.Serialization.StreamingContext"/>) for this serialization.</param>
        private LogEntry(SerializationInfo info, StreamingContext context)
        {
            Id = info.GetInt32("Id");
            Source = info.GetString("Source");
            MessageType = (LoggingMessageType)info.GetInt32("MessageType");
            Message = info.GetString("Message");
            Timestamp = info.GetDateTime("Timestamp");
            Exception = (Exception)info.GetValue("Exception", typeof(Exception));
        }

        #endregion

        #region Methods

        /// <summary>
        /// Translates the <see cref="LoggingMessageType"/> value into the correct three-character prefix that is used in the log files.
        /// </summary>
        /// <param name="messageType">The message type.</param>
        /// <returns>The correct three-character prefix that is used in the log files. This prefix comes with a tab appended to its end.</returns>
        private string GetPrefix(LoggingMessageType messageType)
        {
            // create temp string
            string prefix = "INF";

            switch (messageType)
            {
                case LoggingMessageType.Debug: prefix = "DBG"; break;
                case LoggingMessageType.Trace: { prefix = "TRC"; break; }
                case LoggingMessageType.Info: { prefix = "INF"; break; }
                case LoggingMessageType.Warning: { prefix = "WRN"; break; }
                case LoggingMessageType.Error: { prefix = "ERR"; break; }
                case LoggingMessageType.Exception: { prefix = "EXC"; break; }
            }

            return prefix;
        }

        private LoggingMessageType GetMessageType(string prefix)
        {
            switch (prefix)
            {
                case "DBG": return LoggingMessageType.Debug;
                case "TRC": return LoggingMessageType.Trace;
                case "INF": return LoggingMessageType.Info;
                case "WRN": return LoggingMessageType.Warning;
                case "ERR": return LoggingMessageType.Error;
                case "EXC": return LoggingMessageType.Exception;
                default: return LoggingMessageType.None;
            }
        }

        /// <summary>
        /// Returns this log entry formatted to be used in the log file.
        /// </summary>
        /// <returns>This log entry formatted to be used in the log file.</returns>
        public override string ToString()
        {
            return String.Format(CultureInfo.InvariantCulture, "{0}|{1}|{2}|{3}|{4}", GetPrefix(MessageType), Timestamp, Source, Category, Message);
        }

        #endregion

        #region Factory methods

        /// <summary>
        /// Creates the log entry from its string representation.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static LogEntry CreateFromString(string value)
        {
            LogEntry entry = new LogEntry();
            string[] tokens = value.Split('|');
            if (tokens.Length != 5)
            {
                // malformed
                return null;
            }
            entry.MessageType = entry.GetMessageType(tokens[0]);
            entry.Timestamp = DateTime.Parse(tokens[1], CultureInfo.InvariantCulture);
            entry.Source = tokens[2];
            entry.Category = tokens[3];
            entry.Message= tokens[4];

            return entry;
        }

        #endregion

        #region ISerializable Members

        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (Id.HasValue)
            {
                info.AddValue("Id", Id.Value);
            }
            else
            {
                info.AddValue("Id", 0);
            }
            info.AddValue("Source", Source);
            info.AddValue("MessageType", (int)MessageType);
            info.AddValue("Message", Message);
            info.AddValue("Timestamp", Timestamp);
            info.AddValue("Exception", Exception);
        }

        #endregion

    }
}
